package service

import (
	"encoding/json"
	"fmt"
	"go-project/common"
	"go-project/dto/requrest"
	"go-project/dto/response"
	"go-project/models"
	"log"
	"strconv"
	"strings"
)

type Article struct {
}

func (Article) FindLists(articleSearchRequest *requrest.ArticleSearchRequest) (lists []response.ArticleListResponse, count int64, err error) {
	var article *models.Article
	offset := (articleSearchRequest.Pagination.Page - 1) * articleSearchRequest.Pagination.Limit
	query := common.DB.Table(article.TableName())
	if articleSearchRequest.Title != "" {
		query = query.Where("title like ?", fmt.Sprintf("%%%s%%", articleSearchRequest.Title))
	}
	if articleSearchRequest.CategoryId != "" {
		cateId, _ := strconv.Atoi(articleSearchRequest.CategoryId)
		query = query.Where("category_id=?", cateId)
	}
	query.Model(article).Select("count(id)").Count(&count)
	err = query.Select("articles.*,article_categories.name as category_name,article_categories.id").Joins("left join article_categories on articles.category_id = article_categories.id").Order("articles.id desc").Limit(articleSearchRequest.Pagination.Limit).Offset(offset).Where("articles.deleted_at IS NULL").Scan(&lists).Error
	return
}

func (Article) Add(request *requrest.ArticleInsertRequest) (uint, error) {
	// 开始事务
	tx := common.DB.Begin()
	defer func() {
		if r := recover(); r != nil {
			tx.Rollback()
		}
	}()
	if err := tx.Error; err != nil {
		return 0, err
	}

	var article models.Article
	request.Generate(&article)
	tagIds := strings.Split(request.TagIds, ",")
	var tag models.Tag
	type TagData struct {
		ID    uint   `json:"id"`
		Name  string `json:"name"`
		Color string `json:"color"`
	}
	var tagsData []TagData
	tagsResult := common.DB.Table(tag.TableName()).Select("id,name,color").Where("id in (?)", tagIds).Find(&tagsData)
	if tagsResult.Error != nil {
		return 0, tagsResult.Error
	}
	marshal, err := json.Marshal(tagsData)
	if err != nil {
		return 0, err
	}
	article.Tags = string(marshal)
	articleResult := common.DB.Create(&article)
	if articleResultError := articleResult.Error; articleResultError != nil {
		tx.Rollback()
		return 0, articleResultError
	}

	var valueStrings []string
	var valueArgs []interface{}
	var articleTags models.ArticleTags
	for _, val := range tagIds {
		tagId, _ := strconv.Atoi(val)
		valueStrings = append(valueStrings, "(?, ?, ?)")
		valueArgs = append(valueArgs, articleTags.ID, tagId, article.ID)
	}
	stmt := fmt.Sprintf("INSERT INTO article_tags (id,tag_id, article_id) VALUES %s", strings.Join(valueStrings, ","))
	result := common.DB.Exec(stmt, valueArgs...)
	if err := result.Error; err != nil {
		tx.Rollback()
		return 0, err
	}

	return article.ID, tx.Commit().Error
}

func (Article) Del(id uint) (uint, error) {
	// 开始事务
	tx := common.DB.Begin()
	defer func() {
		if r := recover(); r != nil {
			tx.Rollback()
		}
	}()
	var model models.Article
	result := common.DB.Delete(&model, id)
	if articleErr := result.Error; articleErr != nil {
		tx.Rollback()
		return 0, articleErr
	}
	var articleTags models.ArticleTags
	delRes := common.DB.Unscoped().Where("article_id=?", id).Delete(&articleTags)
	if delResErr := delRes.Error; delResErr != nil {
		tx.Rollback()
		return 0, delResErr
	}
	return uint(result.RowsAffected), tx.Commit().Error
}

func (Article) BatchDelByIds(ids []int) (uint, error) {
	// 开始事务
	tx := common.DB.Begin()
	defer func() {
		if r := recover(); r != nil {
			tx.Rollback()
		}
	}()
	var model models.Article
	result := common.DB.Delete(&model, ids)
	if articleErr := result.Error; articleErr != nil {
		tx.Rollback()
		return 0, articleErr
	}
	var articleTags models.ArticleTags
	delRes := common.DB.Unscoped().Where("article_id", ids).Delete(&articleTags)
	if delResErr := delRes.Error; delResErr != nil {
		tx.Rollback()
		return 0, delResErr
	}
	return uint(result.RowsAffected), tx.Commit().Error
}
func (Article) Get(id uint) (response.ArticleResponse, error) {
	var model models.Article
	var articleRes response.ArticleResponse
	result := common.DB.First(&model, id)
	if err1 := result.Error; err1 != nil {
		return articleRes, err1
	}
	var category models.ArticleCategory
	categoryResult := common.DB.First(&category, model.CategoryId)
	if err1 := categoryResult.Error; err1 != nil {
		return articleRes, err1
	}
	articleRes.ID = model.ID
	articleRes.Title = model.Title
	articleRes.Weight = model.Weight
	articleRes.Tags = model.Tags
	articleRes.Status = model.Status
	articleRes.IsRecommend = model.IsRecommend
	articleRes.CategoryID = model.CategoryId
	articleRes.Content = model.Content
	articleRes.CategoryName = category.Name
	return articleRes, nil
}

func (Article) Update(request *requrest.ArticleUpdateRequest) (uint, error) {
	// 开始事务
	tx := common.DB.Begin()
	defer func() {
		if r := recover(); r != nil {
			tx.Rollback()
		}
	}()
	if err := tx.Error; err != nil {
		return 0, err
	}

	var article models.Article
	first := common.DB.First(&article, request.ID)
	if first.Error != nil {
		tx.Rollback()
		return 0, first.Error
	}
	request.Generate(&article)
	tagIds := strings.Split(request.TagIds, ",")
	var tag models.Tag
	type TagData struct {
		ID    uint   `json:"id"`
		Name  string `json:"name"`
		Color string `json:"color"`
	}
	var tagsData []TagData
	tagsResult := common.DB.Table(tag.TableName()).Select("id,name,color").Where("id in (?)", tagIds).Find(&tagsData)
	if tagsResult.Error != nil {
		return 0, tagsResult.Error
	}
	marshal, err := json.Marshal(tagsData)
	if err != nil {
		return 0, err
	}
	article.Tags = string(marshal)
	articleResult := common.DB.Save(&article)
	if articleResultError := articleResult.Error; articleResultError != nil {
		tx.Rollback()
		return 0, articleResultError
	}
	var articleTags models.ArticleTags
	var articlesTags []models.ArticleTags
	articleTegRes := common.DB.Table(articleTags.TableName()).Where("article_id =?", article.ID).Find(&articlesTags)
	if tegResErr := articleTegRes.Error; tegResErr != nil {
		tx.Rollback()
		return 0, tegResErr
	}
	var insertTagsIds []uint
	for _, id := range tagIds {
		atoi, _ := strconv.Atoi(id)
		id := uint(atoi)
		isExists := false
		for _, articlesTag := range articlesTags {
			log.Println(articlesTag, "articlesTag")
			if articlesTag.TagId == id {
				isExists = true
				break
			}
		}
		if !isExists {
			insertTagsIds = append(insertTagsIds, id)
		}
	}
	delRes := common.DB.Unscoped().Not("tag_id", tagIds).Where("article_id=?", article.ID).Delete(&articleTags)
	if delResErr := delRes.Error; delResErr != nil {
		tx.Rollback()
		return 0, delResErr
	}
	if len(insertTagsIds) > 0 {
		var valueStrings []string
		var valueArgs []interface{}
		for _, val := range insertTagsIds {
			valueStrings = append(valueStrings, "(?, ?, ?)")
			valueArgs = append(valueArgs, articleTags.ID, val, article.ID)
		}
		stmt := fmt.Sprintf("INSERT INTO article_tags (id,tag_id, article_id) VALUES %s", strings.Join(valueStrings, ","))
		result := common.DB.Exec(stmt, valueArgs...)
		if err := result.Error; err != nil {
			tx.Rollback()
			return 0, err
		}
	}

	return article.ID, tx.Commit().Error
}
